--	FROM TTT
--	IronSights by McFly
--	Other view / movements by McFly

if SERVER then
   AddCSLuaFile( "shared.lua" )
   AddCSLuaFile( "special_actions.lua" )
end

if CLIENT then
   SWEP.DrawCrosshair   = false
   SWEP.ViewModelFOV    = 82
   SWEP.ViewModelFlip   = true
   SWEP.CSMuzzleFlashes = true
end

include( "special_actions.lua" )

SWEP.Base = "weapon_base"

SWEP.Category           = "Sigma.Core"
SWEP.Spawnable          = false
SWEP.AdminSpawnable     = false

SWEP.Weight             = 5
SWEP.AutoSwitchTo       = false
SWEP.AutoSwitchFrom     = false

SWEP.Primary.Sound          = Sound( "Weapon_Pistol.Empty" )
SWEP.Primary.Recoil         = 1.5
SWEP.Primary.Damage         = 1
SWEP.Primary.NumShots       = 1
SWEP.Primary.Cone           = 0.02
SWEP.Primary.Delay          = 0.15

SWEP.Primary.ClipSize       = -1
SWEP.Primary.DefaultClip    = -1
SWEP.Primary.Automatic      = false
SWEP.Primary.Ammo           = "none"
SWEP.Primary.ClipMax        = -1

SWEP.Secondary.ClipSize     = 1
SWEP.Secondary.DefaultClip  = 1
SWEP.Secondary.Automatic    = false
SWEP.Secondary.Ammo         = "none"
SWEP.Secondary.ClipMax      = -1

SWEP.HeadshotMultiplier = 2.7

SWEP.StoredAmmo = 0
SWEP.IsDropped = false

SWEP.DeploySpeed = 1.4

SWEP.PrimaryAnim = ACT_VM_PRIMARYATTACK
SWEP.ReloadAnim = ACT_VM_RELOAD

SWEP.IronSightsPos 		= Vector( 0, 0, 0 )
SWEP.IronSightsAng 		= Vector( 0, 0, 0 )

AccessorFuncDT( SWEP, "shooting", "Shooting" )
AccessorFuncDT( SWEP, "ironsights", "Ironsights" )

local sparkle = CLIENT and CreateConVar( "sc_sparkles", "0", FCVAR_ARCHIVE )

function SWEP:PrimaryAttack(worldsnd)

   self.Weapon:SetNextSecondaryFire( CurTime() + self.Primary.Delay )
   self.Weapon:SetNextPrimaryFire( CurTime() + self.Primary.Delay )

   if not self:CanPrimaryAttack() then return end

   if not worldsnd then
      self.Weapon:EmitSound( self.Primary.Sound, self.Primary.SoundLevel )
   elseif SERVER then
      sound.Play(self.Primary.Sound, self:GetPos(), self.Primary.SoundLevel)
   end

   self:ShootBullet( self.Primary.Damage, self.Primary.Recoil, self.Primary.NumShots, self:GetPrimaryCone() )

   self:TakePrimaryAmmo( 1 )
   
   self.ShootingTime = CurTime()
   self:SetShooting( true )

   local owner = self.Owner
   if not IsValid(owner) or owner:IsNPC() or (not owner.ViewPunch) then return end

   owner:ViewPunch( Angle( math.Rand(-0.2,-0.1) * self.Primary.Recoil, math.Rand(-0.1,0.1) *self.Primary.Recoil, 0 ) )
   
end

function SWEP:DryFire( setnext )
   if CLIENT and LocalPlayer() == self.Owner then
      self:EmitSound( "Weapon_Pistol.Empty" )
   end

   setnext( self, CurTime() + 0.2 )

   self:Reload()
end

function SWEP:CanPrimaryAttack()
   if not IsValid(self.Owner) then return end

   if self.Weapon:Clip1() <= 0 then
      self:DryFire( self.SetNextPrimaryFire )
      return false
   end
   return not self:CheckHitsWall().Hit
end

function SWEP:CanSecondaryAttack()
   if not IsValid(self.Owner) then return end

   if self.Weapon:Clip2() <= 0 then
      self:DryFire( self.SetNextSecondaryFire )
      return false
   end
   return true
end

local function Sparklies(attacker, tr, dmginfo)
   if tr.HitWorld and tr.MatType == MAT_METAL then
      local eff = EffectData()
      eff:SetOrigin(tr.HitPos)
      eff:SetNormal(tr.HitNormal)
      util.Effect("cball_bounce", eff)
   end
end

function SWEP:ShootBullet( dmg, recoil, numbul, cone )

   self.Weapon:SendWeaponAnim(self.PrimaryAnim)

   self.Owner:MuzzleFlash()
   self.Owner:SetAnimation( PLAYER_ATTACK1 )

   if not IsFirstTimePredicted() then return end

   local sights = self:GetIronsights()

   numbul = numbul or 1
   cone   = cone   or 0.01

   local bullet = {}
   bullet.Num    = numbul
   bullet.Src    = self.Owner:GetShootPos()
   bullet.Dir    = self.Owner:GetAimVector()
   bullet.Spread = Vector( cone, cone, 0 )
   --bullet.Tracer = 4
   --bullet.TracerName = self.Tracer or "Tracer"
   bullet.Force  = 10
   bullet.Damage = dmg
   if CLIENT and sparkle:GetBool() then
      bullet.Callback = Sparklies
   end

   self.Owner:FireBullets( bullet )

   if (not IsValid(self.Owner)) or (not self.Owner:Alive()) or self.Owner:IsNPC() then return end

   if ((game.SinglePlayer() and SERVER) or
       ((not game.SinglePlayer()) and CLIENT and IsFirstTimePredicted())) then

      recoil = sights and (recoil * 0.6) or recoil

      local eyeang = self.Owner:EyeAngles()
      eyeang.pitch = eyeang.pitch - recoil
      self.Owner:SetEyeAngles( eyeang )
   end

end

function SWEP:GetPrimaryCone()
   local cone = self.Primary.Cone or 0.2
   return self:GetIronsights() and (cone * 0.85) or cone
end

function SWEP:GetHeadshotMultiplier(victim, dmginfo)
   return self.HeadshotMultiplier
end

function SWEP:IsEquipment()
   return WEPS.IsEquipment(self)
end

function SWEP:DrawWeaponSelection() end

function SWEP:SecondaryAttack()
   if self.NoSights or (not self.IronSightsPos) then return end

   self:SetIronsights( true )

   self:SetNextSecondaryFire(CurTime() + 0.3)
end

function SWEP:Deploy()
   self:SetIronsights(false)
   self:SetShooting(false)
   return true
end

function SWEP:Reload()
   self.Weapon:DefaultReload(self.ReloadAnim)
   self:SetIronsights( false )
   self:SetShooting(false)
end


function SWEP:OnRestore()
   self.NextSecondaryAttack = 0
   self:SetIronsights( false )
   self:SetShooting(false)
end

function SWEP:Ammo1()
   return IsValid(self.Owner) and self.Owner:GetAmmoCount(self.Primary.Ammo) or false
end

function SWEP:PreDrop()
   if SERVER and IsValid(self.Owner) and self.Primary.Ammo != "none" then
      local ammo = self:Ammo1()

      for _, w in pairs(self.Owner:GetWeapons()) do
         if IsValid(w) and w != self and w:GetPrimaryAmmoType() == self:GetPrimaryAmmoType() then
            ammo = 0
         end
      end

      self.StoredAmmo = ammo

      if ammo > 0 then
         self.Owner:RemoveAmmo(ammo, self.Primary.Ammo)
      end
   end
end

function SWEP:DampenDrop()
   local phys = self:GetPhysicsObject()
   if IsValid(phys) then
      phys:SetVelocityInstantaneous(Vector(0,0,-75) + phys:GetVelocity() * 0.001)
      phys:AddAngleVelocity(phys:GetAngleVelocity() * -0.99)
   end
end

function SWEP:Equip(newowner)
   if SERVER then
      if self:IsOnFire() then
         self:Extinguish()
      end
   end

   if SERVER and IsValid(newowner) and self.StoredAmmo > 0 and self.Primary.Ammo != "none" then
      local ammo = newowner:GetAmmoCount(self.Primary.Ammo)
      local given = math.min(self.StoredAmmo, self.Primary.ClipMax - ammo)

      newowner:GiveAmmo( given, self.Primary.Ammo)
      self.StoredAmmo = 0
   end
end

function SWEP:SetupDataTables()
	self:DTVar("Bool", 2, "shooting")
	self:DTVar("Bool", 3, "ironsights")
end

function SWEP:Initialize()
   if CLIENT and self.Weapon:Clip1() == -1 then
      self.Weapon:SetClip1(self.Primary.DefaultClip)
   elseif SERVER then
      self:SetIronsights(false)
	  self:SetShooting(false)
   end

   self:SetDeploySpeed(self.DeploySpeed)
   self.ShootingTime = CurTime()

   if self.SetWeaponHoldType then
      self:SetWeaponHoldType(self.HoldType or "pistol")
   end
end

function SWEP:Think()

	if self.Owner:KeyReleased( IN_ATTACK2 ) and self:GetIronsights() then
		self:SetIronsights( false )
	end
	
	if SERVER and self:GetShooting() and CurTime() - self.ShootingTime > 0.5 then
		self:SetShooting(false)
	end
	
	self.DrawCrosshair = not tobool( GetConVarNumber( "sc_custom_crosshair" ) )
end

function SWEP:DyingShot()
   local fired = false
   if self:GetIronsights() then
      self:SetIronsights(false)

      if self.Weapon:GetNextPrimaryFire() > CurTime() then
         return fired
      end

      if IsValid(self.Owner) then
         local punch = self.Primary.Recoil or 5

         local eyeang = self.Owner:EyeAngles()
         eyeang.pitch = eyeang.pitch - math.Rand(-punch, punch)
         eyeang.yaw = eyeang.yaw - math.Rand(-punch, punch)
         self.Owner:SetEyeAngles( eyeang )

         MsgN(self.Owner:Nick() .. " fired his DYING SHOT")

         self.Owner.dying_wep = self.Weapon

         self:PrimaryAttack(true)

         fired = true
      end
   end

   return fired
end

function SWEP:GetViewModelPosition( pos, ang )
	
	/* Init last frame time if needed */
	if not self.LastFrameTime then self.LastFrameTime = CurTime() end
	
	/* Time elapsed since last frame */
	local timeElapsed = CurTime() - self.LastFrameTime
	
	/* Calculate pos and ang for Iron Sights */
	pos, ang = self:CalculateIronSights( timeElapsed, pos, ang )
	
	/* Calculate pos and ang for Running */
	pos, ang = self:CalculateRunning( timeElapsed, pos, ang )
	
	/* Calculate pos and ang for close walls */
	pos, ang = self:CalculateWalls( pos, ang )
	
	/* Update last frame time */
	self.LastFrameTime = CurTime()
	
	/* Return new values */
	return pos, ang

end

function SWEP:CalculateIronSights( timeElapsed, pos, ang )

	/* Locals */
	local sightsOn		= self:GetIronsights()
	local shooting		= self:GetShooting()
	local sightsTime	= 0
	local sightsPos		= self.IronSightsPos
	local sightsAng		= self.IronSightsAng
	
	/* Get the total time to switch */
	if sightsOn then
		sightsTime		= 0.3
	else
		sightsTime		= 0.3
	end
	
	/* Iron sights are active */
	if sightsOn then
	
		/* If the elapsed time hasn't been created yet, do it */
		self.IronSightsElapsed = self.IronSightsElapsed or 0
		
		/* Update elapsed */
		self.IronSightsElapsed = math.Clamp( self.IronSightsElapsed + timeElapsed, 0, sightsTime )
		
	/* Iron sights are inactive */
	else
	
		/* If the elapsed time hasn't been created yet, do it */
		self.IronSightsElapsed = self.IronSightsElapsed or sightsTime
		
		/* Update elapsed */
		self.IronSightsElapsed = math.Clamp( self.IronSightsElapsed - timeElapsed, 0, sightsTime )
		
	end
	
	/* Calculate multiplier (ratio: 0 <= mul <= 1) */
	local mul = self.IronSightsElapsed / sightsTime
	
	/* Calculate angles */
	ang:RotateAroundAxis( ang:Right(), sightsAng.x * mul )
	ang:RotateAroundAxis( ang:Up(), sightsAng.y * mul )
	ang:RotateAroundAxis( ang:Forward(), sightsAng.z * mul )
	
	/* Calculate position */
	pos = pos + sightsPos.x * ang:Right() * mul
	pos = pos + sightsPos.y * ang:Forward() * mul
	pos = pos + sightsPos.z * ang:Up() * mul
	
	/* Return new values */
	return pos, ang
end

/* Calculate pos and ang for Running */
function SWEP:CalculateRunning( timeElapsed, pos, ang )

	/* Locals */
	local shooting		= self:GetShooting()
	local ironsights	= self:GetIronsights()
	local runningTime	= 0
	local runningPos	= Vector( 1.09, -4.94, -0.08 )
	local runningAng	= Vector( -15.95, -52.27, 16.51 )
	local vel			= self.Owner:GetVelocity():Length()
	
	/* Do nothing if we're not shooting or are using sights */
	if shooting or ironsights then return pos, ang end
	
	/* Calculate multiplier (ratio: 0 <= mul <= 1) */
	local mul = vel / 250
	
	/* Add smoothness */
	self.LastHitRunMul		= self.LastHitRunMul or mul
	mul						= self.LastHitRunMul - ( self.LastHitRunMul - mul ) * 0.25
	mul						= math.Round( mul, 4 )
	self.LastHitRunMul		= mul
	
	/* Calculate angles */
	ang:RotateAroundAxis( ang:Right(), runningAng.x * mul )
	ang:RotateAroundAxis( ang:Up(), runningAng.y * mul )
	ang:RotateAroundAxis( ang:Forward(), runningAng.z * mul )
	
	/* Calculate position */
	pos = pos + runningPos.x * ang:Right() * mul
	pos = pos + runningPos.y * ang:Forward() * mul
	pos = pos + runningPos.z * ang:Up() * mul
	
	/* Return new values */
	return pos, ang
end

/* Calculate pos and ang for close walls */
function SWEP:CalculateWalls( pos, ang )

	/* Check if the SWEP hits a wall */
	local trace			= self:CheckHitsWall()
	local mul			= 0
	local offsetPos		= Vector( 0, -11.48, -6.59 )
	local offsetAng		= Vector( 55.72, 0, 0 )
	local len			= 75
	
	/* Calculate multiplier (ratio: 0 <= mul <= 1) */
	if trace.Hit then
		local trLen			= trace.HitPos - self.Owner:GetShootPos()
		local trSqX			= math.pow( trLen.x, 2 )
		local trSqY			= math.pow( trLen.y, 2 )
		local trSqZ			= math.pow( trLen.z, 2 )
		local length		= math.sqrt( trSqX + trSqY + trSqZ )
		mul					= 1 - ( length / len )
	end
	
	/* Add smoothness */
	self.LastWallHitMul		= self.LastWallHitMul or mul
	mul						= self.LastWallHitMul - ( self.LastWallHitMul - mul ) * 0.135
	mul						= math.Round( mul, 4 )
	self.LastWallHitMul		= mul
	
	/* Calculate angles */
	ang:RotateAroundAxis( ang:Right(), offsetAng.x * mul )
	ang:RotateAroundAxis( ang:Up(), offsetAng.y * mul )
	ang:RotateAroundAxis( ang:Forward(), offsetAng.z * mul )
	
	/* Calculate position */
	pos = pos + offsetPos.x * ang:Right() * mul
	pos = pos + offsetPos.y * ang:Forward() * mul
	pos = pos + offsetPos.z * ang:Up() * mul
	
	/* Return new values */
	return pos, ang
end